/******************************************************************************
 * ALi (Zhuhai) Corporation, All Rights Reserved. 2014 Copyright (C)
 *
 * File: rbuffer.c
 *
 * Description: -
 *     ring buffer module.
 * History
 *       Date            Author             Version           Comment
 *     2014/02/18        Doy.Dong              1              create
 *
 ******************************************************************************/

#include <basic_types.h>
#include <os/tds2/itron.h>
#include <osal/osal_timer.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <osal/osal.h>
#include <api/libc/alloc.h>
#include <api/libfs2/types.h>
#include <api/libcurl/rbuffer.h>

#define RBUFF_PTR_INC(P,S,O) ((P)+(O)<(S)?(P)+(O):(P)+(O)-(S))
#define RBUFF_PTR_DEC(P,S,O) ((P)-(O)>=0?(P)-(O):(S)+(P)-(O))

int rbuffer_init(rbuffer *rb, char *buffer, int size)
{
    if (rb == NULL || size <= 0)
    {
        return 0;
    }
    rb->wr = rb->rd = 0;
    rb->size = size;
    if (buffer) { rb->buff = buffer; }
    if (rb->buff == NULL)
    {
        rb->buff = MALLOC(size);
        rb->allocb = 1;
    }
    rb->wpb = 1;
    return (rb->buff != NULL);
}

void rbuffer_destroy(rbuffer *rb)
{
    if (rb == NULL) { return; }

    if (rb->buff && rb->allocb)
    {
        FREE(rb->buff);
        rb->buff = NULL;
    }
}

void rbuffer_set_wpb(rbuffer *rb, int wpb)
{
    if (rb == NULL) { return ; }
    if (wpb < 1) { wpb = 1; }
    if (wpb < rb->size)
    {
        rb->wpb = wpb;
    }
}

int rbuffer_dlen(rbuffer *rb)
{
    int r = rb->rd;
    int w = rb->wr;

    if (w >= r)
    {
        return (w - r);
    }
    else
    {
        return (w + rb->size - r);
    }
}

int rbuffer_flen(rbuffer *rb)
{
    int left = 0;
    int r = rb->rd;
    int w = rb->wr;

    if (w >= r)
    {
        left = r + rb->size - w;
    }
    else
    {
        left = r - w;
    }

    left = (left > rb->wpb) ? (left - rb->wpb) : 0;
    return left;
}

int rbuffer_olen(rbuffer *rb)
{
    int left = 0;
    int r = rb->rd;
    int w = rb->wr;
    left = (w >= r) ? (r + rb->size - w) : (r - w);
    left = (left > 1) ? (left - 1) : 0;
    return left;
}

void rbuffer_clean(rbuffer *rb)
{
    rb->rd = rb->wr = 0;
}

int rbuffer_write(rbuffer *rb, char *data, int len)
{
    int wlen = 0;
    int r = rb->rd;
    int w = rb->wr;

    wlen = (w >= r) ? (r + rb->size - w) : (r - w); //free space length
    wlen -= rb->wpb;
    if (wlen <= 0 || len <= 0 || data == NULL) { return 0; }

    wlen = (len > wlen) ? wlen : len;
    if (w < r)
    {
        MEMCPY(rb->buff + w, data, wlen);
    }
    else
    {
        int s = rb->size - w;
        if (wlen <= s)
        {
            MEMCPY(rb->buff + w, data, wlen);
        }
        else
        {
            MEMCPY(rb->buff + w, data, s);
            MEMCPY(rb->buff, data + s, wlen - s);
        }
    }

    rb->wr = RBUFF_PTR_INC(w, rb->size, wlen);
    return wlen;
}

int rbuffer_read(rbuffer *rb, char *out, int len)
{
    int rlen = 0;
    int r = rb->rd;
    int w = rb->wr;

    rlen = (w >= r) ? (w - r) : (w + rb->size - r); // data length
    rlen = len > rlen ? rlen : len;
    if (rlen <= 0 || len <= 0 || out == NULL) { return 0; }

    if (r < w)
    {
        MEMCPY(out, rb->buff + r, rlen);
    }
    else
    {
        int s = rb->size - r;
        if (rlen <= s)
        {
            MEMCPY(out, rb->buff + r, rlen);
        }
        else
        {
            MEMCPY(out, rb->buff + r, s);
            MEMCPY(out + s, rb->buff, rlen - s);
        }
    }
    rb->rd = RBUFF_PTR_INC(r, rb->size, rlen);
    return rlen;
}

int rbuffer_seek(rbuffer *rb, int offset)
{
    int ret = 0;
    if (offset > 0 && offset <= rbuffer_dlen(rb))
    {
        rb->rd = RBUFF_PTR_INC(rb->rd, rb->size, offset);
    }
    else if (offset < 0 && (-1 * offset) <= rbuffer_olen(rb))
    {
        rb->rd = RBUFF_PTR_DEC(rb->rd, rb->size, offset);
    }
    else
    {
        ret = -1;
    }
    return ret;
}

